package com.ytjj.qmyx.supplychain.service;

import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageHelper;
import com.ytjj.qmyx.supplychain.common.api.CommonPage;
import com.ytjj.qmyx.supplychain.common.api.CommonResult;
import com.ytjj.qmyx.supplychain.common.enums.*;
import com.ytjj.qmyx.supplychain.common.model.bill.request.*;
import com.ytjj.qmyx.supplychain.common.model.bill.response.*;
import com.ytjj.qmyx.supplychain.common.utils.RedisService;
import com.ytjj.qmyx.supplychain.mapper.*;
import com.ytjj.qmyx.supplychain.mapper.entity.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import tk.mybatis.mapper.entity.Example;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

@Slf4j
@Service
public class BillService {

    @Resource
    private BillMapper billMapper;

    @Resource
    private BillPaymentMapper billPaymentMapper;

    @Resource
    private BankerService bankerService;

    @Resource
    private OrderKafkaRecordMapper orderKafkaRecordMapper;

    @Resource
    private OrdersMapper ordersMapper;

    @Resource
    private BillOrderMapper billOrderMapper;

    @Resource
    private BillRefundMapper billRefundMapper;

    @Resource
    private BillProductMapper billProductMapper;


    public CommonResult create(CreateBillRequest request) {
        Assert.isTrue(StringUtils.isNotBlank(request.getBillStartTime()),"账单开始时间不能为空");
        Assert.isTrue(StringUtils.isNotBlank(request.getBillEndTime()),"账单结束时间不能为空");

        AtomicReference<Boolean> flag = new AtomicReference<>(false);
        try {
            Example example = new Example(YxBill.class);
            Example.Criteria criteria = example.createCriteria();
            criteria.andEqualTo("bankerId", request.getBankerId());//.andIn("billStatus", Arrays.asList(1,2));
            example.selectProperties("billStartTime", "billEndTime");
            List<YxBill> yxBills = billMapper.selectByExample(example);
            //检查是否存在与以往的账单时间交集
            flag.set(checkBillTime(yxBills,request.getBillStartTime(),request.getBillEndTime(), flag));
        } catch (ParseException e) {
            e.printStackTrace();
        }
        if (flag.get()){
            return CommonResult.failed("账单时间与以往账单时间存在重复，请修改");
        }

        Example example = new Example(YxBill.class);
        example.createCriteria().andEqualTo("bankerId",request.getBankerId()).andIn("billStatus", Arrays.asList(1,2));
        if (billMapper.selectCountByExample(example) > 0){
            return CommonResult.failed("请先将该供应商“对账中”的对账单完成对账，再创建新的对账单。");
        }

        BankerBillResponse bankerInfo = bankerService.getBankerById(request.getBankerId());
        if (null == bankerInfo){
            return CommonResult.failed("查询不到供应商信息");
        }
        //账单表
        YxBill yxBill = new YxBill();

        BeanUtils.copyProperties(request,yxBill);
        yxBill.setBillNo(getBankerBillNo(request.getBankerId()));
        yxBill.setBankerName(bankerInfo.getBankerName());
        yxBill.setCreateTime(new Date());
        yxBill.setBillStatus(1);
        billMapper.insertSelective(yxBill);
        //账单打款表
        YxBillPayment yxBillPayment = new YxBillPayment();
        yxBillPayment.setBillId(yxBill.getId());
        yxBillPayment.setCreateTime(new Date());
        yxBillPayment.setPaymentStatus(0);
        yxBillPayment.setBankHouse(bankerInfo.getBankHouse());
        yxBillPayment.setCreditCardNumbers(bankerInfo.getCreditCardNumbers());
        yxBillPayment.setCreditCartPerson(bankerInfo.getCreditCartPerson());
        billPaymentMapper.insertSelective(yxBillPayment);

        String startTime = yxBill.getBillStartTime() + " 00:00:00";
        String endTime = yxBill.getBillEndTime() + " 23:59:59";
        /**
         * 根据供应商id和时间查询订单并把订单的记录起来
         */
        BillMoneyDTO dto = saveOrderAndAfterSale(yxBill.getBillNo(), yxBill.getBankerId(), startTime, endTime);
        //金额保存起来
        updateBill(yxBill,dto);

//        //从大数据中获取金额并保存起来
//        getAmountByBigData(yxBill);
//        //把传送kafka记录表标记起来
//        signRecord(yxBill.getBankerId(), yxBill.getId(),yxBill.getBillStartTime(),yxBill.getBillEndTime());
        return CommonResult.success();
    }

    /**
     * 检查是否存在与以往的账单时间交集
     * @param billStartTime
     * @param billEndTime
     * @param flag
     * @return
     * @throws ParseException
     */
    private Boolean checkBillTime(List<YxBill> yxBills,
                                  String billStartTime,
                                  String billEndTime,
                                  AtomicReference<Boolean> flag) throws ParseException {

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date requestStartTime = sdf.parse(billStartTime + " 00:00:00");
        Date requestEndTime = sdf.parse(billEndTime + " 23:59:59");
        yxBills.forEach(e ->{
            if (flag.get()){
                return;
            }
            Date oldStartTime = null;
            Date oldEndTime = null;
            try {
                oldStartTime = sdf.parse(e.getBillStartTime() + " 00:00:00");
                oldEndTime = sdf.parse(e.getBillEndTime() + " 23:59:59");
            } catch (ParseException parseException) {
                parseException.printStackTrace();
            }
            if (oldStartTime.getTime() <= requestStartTime.getTime() && requestStartTime.getTime() <= oldEndTime.getTime()){
                flag.set(true);
            }
            if (oldStartTime.getTime() <= requestEndTime.getTime() && requestEndTime.getTime() <= oldEndTime.getTime()){
                flag.set(true);
            }
        });
        return flag.get();
    }

    @Resource
    private RedisService redisService;

    /**
     * 生成账单id (供应商id+0000递增)
     *
     * @param bankerId
     * @return
     */
    private String getBankerBillNo(Integer bankerId) {

        String redisKey = "banker:banker_" + bankerId+ "_billNum_";
        String orderNumStr = redisService.get(redisKey);
        int orderNum = 1;
        if (StringUtils.isNotBlank(orderNumStr)) {
            orderNum = Integer.parseInt(orderNumStr);
            redisService.increment(redisKey, 1);
        }else {
            redisService.set(redisKey,"1");
        }
        DecimalFormat df = new DecimalFormat("0000");
        String format = df.format(orderNum);
        return bankerId + format;
    }

    /**
     * 根据供应商id和时间查询订单并把订单的记录起来
     * @param bankerId
     * @param startTime
     * @param endTime
     */
    private BillMoneyDTO saveOrderAndAfterSale(String billNo, Integer bankerId, String startTime, String endTime) {
        BillMoneyDTO billMoneyDTO = new BillMoneyDTO();
        List<OrderToBillResponse> list = new ArrayList<>();
        //该时间内所有应付
        List<OrderToBillResponse> orderList = ordersMapper.selectBillOrder(bankerId,startTime,endTime);
        if (!CollectionUtils.isEmpty(orderList)) {
            //总应付成本
            BigDecimal orderTotalMoney = orderList.stream().filter(item ->null != item.getCostPrice()).map(OrderToBillResponse::getCostPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
            billMoneyDTO.setOrderTotalMoney(null != orderTotalMoney ? orderTotalMoney:BigDecimal.ZERO);
            //总应付运费
            BigDecimal orderFreightTotalMoney = orderList.stream().filter(item ->null != item.getFreight()).map(OrderToBillResponse::getFreight).reduce(BigDecimal.ZERO, BigDecimal::add);
            billMoneyDTO.setOrderFreightTotalMoney(null != orderFreightTotalMoney ? orderFreightTotalMoney:BigDecimal.ZERO);

            List<BillOrder> billOrders = orderList.stream().map(item -> {
                BillOrder billOrder = new BillOrder();
                BeanUtils.copyProperties(item, billOrder);
                billOrder.setBillNo(billNo);
                String substring = item.getChildOrderNo().substring(0, 1);
                switch (substring){
                    case "MZ":
                        billOrder.setProject(ProjectEnum.TXG.getCode());
                        break;
                    case "Q":
                        billOrder.setProject(ProjectEnum.YX.getCode());
                        break;
                    default:
                        break;
                }
                return billOrder;
            }).collect(Collectors.toList());
            billOrderMapper.insertBatch(billOrders);
            list.addAll(orderList);
        }
        //该时间内所有应退
        List<OrderToBillResponse> refundList = ordersMapper.selectBillRefund(bankerId,startTime,endTime);
        if (!CollectionUtils.isEmpty(refundList)){
            //总应退成本
            BigDecimal refundTotalMoney = BigDecimal.ZERO;
            //总应退运费
            BigDecimal refundFreightTotalMoney = BigDecimal.ZERO;
            refundFreightTotalMoney = refundList.stream().filter(item -> null != item.getFreightDuty() && FreightDutyEnum.WAREHOUSE.getCode().equals(item.getFreightDuty())).map(OrderToBillResponse::getFreightDutyFee).reduce(BigDecimal.ZERO, BigDecimal::add);
            //往期未对账的售后完成订单
            List<OrderToBillResponse> collect = refundList.stream().filter(item -> OrderReconciliationEnum.NO_CHECK.getCode().equals(item.getOrderReconciliationStatus())).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(collect)){
                refundTotalMoney = collect.stream().filter(item -> null != item.getReturnDuty() && ReturnDutyEnum.WAREHOUSE.getCode().equals(item.getReturnDuty())).map(OrderToBillResponse::getWarehouseBearFee).reduce(BigDecimal.ZERO, BigDecimal::add);
                List<String> childOrderNoList = collect.stream().map(OrderToBillResponse::getChildOrderNo).collect(Collectors.toList());
                //往期已对账的售后完成订单
                List<OrderToBillResponse> collect1 = refundList.stream().filter(item -> !childOrderNoList.contains(item.getChildOrderNo())).collect(Collectors.toList());
                if (!CollectionUtils.isEmpty(collect1)){
                    BigDecimal refundMoney = collect1.stream().map(OrderToBillResponse::getCostPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
                    refundTotalMoney = refundTotalMoney.add(refundMoney);
                }
            }
            billMoneyDTO.setRefundTotalMoney(null != refundTotalMoney ? refundTotalMoney:BigDecimal.ZERO );
            billMoneyDTO.setRefundFreightTotalMoney(null != refundFreightTotalMoney ? refundFreightTotalMoney:BigDecimal.ZERO );
            List<BillRefund> billRefunds = refundList.stream().map(item -> {
                BillRefund billRefund = new BillRefund();
                BeanUtils.copyProperties(item,billRefund);
                billRefund.setBillNo(billNo);
                String substring = item.getChildOrderNo().substring(0, 1);
                switch (substring){
                    case "MZ":
                        billRefund.setProject(ProjectEnum.TXG.getCode());
                        break;
                    case "Q":
                        billRefund.setProject(ProjectEnum.YX.getCode());
                        break;
                    default:
                        break;
                }
                return billRefund;
            }).collect(Collectors.toList());
            billRefundMapper.insertBatch(billRefunds);
            list.addAll(refundList);
        }
        if (!CollectionUtils.isEmpty(list)){
            log.info("===============List<OrderToBillResponse> list================{}", JSONObject.toJSONString(list));
            List<BillProduct> collect = list.stream().map(item -> {
                BillProduct billProduct = new BillProduct();
                BeanUtils.copyProperties(item, billProduct);
                long spuNum = list.stream().filter(spu -> spu.getProductId().equals(item.getProductId())).count();
                long skuNum = list.stream().filter(sku ->  null != sku.getSkuId() &&null != item.getSkuId() && sku.getSkuId().equals(item.getSkuId())).count();
                billProduct.setSpuNum(Math.toIntExact(spuNum));
                billProduct.setSkuNum(Math.toIntExact(skuNum));
                billProduct.setBillNo(billNo);
                return billProduct;
            }).distinct().collect(Collectors.toList());
            billProductMapper.insertBatch(collect);
        }
        return billMoneyDTO;
    }

    private void updateBill(YxBill yxBill,BillMoneyDTO dto){
        //保存起来
        yxBill.setPayableTotalAmount((null != dto.getOrderTotalMoney() ? dto.getOrderTotalMoney() : BigDecimal.ZERO).add((null != dto.getOrderFreightTotalMoney() ? dto.getOrderFreightTotalMoney() : BigDecimal.ZERO)));
        yxBill.setRefundableAmount(null != dto.getRefundTotalMoney() ? dto.getRefundTotalMoney() : BigDecimal.ZERO);
        yxBill.setRefundableFreight(null != dto.getRefundFreightTotalMoney() ? dto.getRefundFreightTotalMoney() : BigDecimal.ZERO);
        yxBill.setPayableFreight((null != dto.getOrderFreightTotalMoney() ? dto.getOrderFreightTotalMoney() : BigDecimal.ZERO).subtract((null != dto.getRefundFreightTotalMoney() ? dto.getRefundFreightTotalMoney() : BigDecimal.ZERO)));
        yxBill.setPayableAmount((null != dto.getOrderTotalMoney() ? dto.getOrderTotalMoney() : BigDecimal.ZERO).subtract((null != dto.getRefundTotalMoney() ? dto.getRefundTotalMoney() : BigDecimal.ZERO)));
        yxBill.setTotalAmount(yxBill.getPayableTotalAmount().subtract(yxBill.getRefundableAmount()).subtract(yxBill.getRefundableFreight()));
        billMapper.updateByPrimaryKeySelective(yxBill);
    }

    /**
     * 把传送kafka记录表标记起来
     * @param bankerId
     * @param billId
     * @param billStartTime
     * @param billEndTime
     */
    public void signRecord(Integer bankerId, Integer billId, String billStartTime, String billEndTime){
        billStartTime = billStartTime + " 00:00:00";
        billEndTime = billEndTime + " 23:59:59";
        orderKafkaRecordMapper.signRecord(bankerId, billId, billStartTime, billEndTime);
    }

    public CommonResult edit(EditBillRequest request) {
        Assert.isTrue(StringUtils.isNotBlank(request.getBillStartTime()),"账单开始时间不能为空");
        Assert.isTrue(StringUtils.isNotBlank(request.getBillEndTime()),"账单结束时间不能为空");

        AtomicReference<Boolean> flag = new AtomicReference<>(false);
        try {
            Example example = new Example(YxBill.class);
            Example.Criteria criteria = example.createCriteria();
            criteria.andEqualTo("bankerId", request.getBankerId());//.andIn("billStatus", Arrays.asList(1,2));
            if (null != request.getId()){
                criteria.andNotEqualTo("id",request.getId());
            }
            example.selectProperties("billStartTime", "billEndTime");

            //不能重复创建相同时间内的账单
            List<YxBill> yxBills = billMapper.selectByExample(example);
            //检查是否存在与以往的账单时间交集
            flag.set(checkBillTime(yxBills,request.getBillStartTime(),request.getBillEndTime(), flag));
        } catch (ParseException e) {
            e.printStackTrace();
        }
        if (flag.get()){
            return CommonResult.failed("账单时间与以往账单时间存在重复，请修改");
        }

        YxBill yxBill = billMapper.selectByPrimaryKey(request.getId());
        if (1 != yxBill.getBillStatus()){
            return CommonResult.failed("对账完成的账单不能修改");
        }

        if (!request.getBankerId().equals(yxBill.getBankerId())){
            return CommonResult.failed("对账数据异常");
        }

        BeanUtils.copyProperties(request,yxBill);
        billMapper.updateByPrimaryKeySelective(yxBill);
        String startTime = yxBill.getBillStartTime() + " 00:00:00";
        String endTime = yxBill.getBillEndTime() + " 23:59:59";
        //把原本账单信息删除
        Example orderExample = new Example(BillOrder.class);
        orderExample.createCriteria().andEqualTo("billNo",yxBill.getBillNo());
        billOrderMapper.deleteByExample(orderExample);
        Example refundExample = new Example(BillRefund.class);
        refundExample.createCriteria().andEqualTo("billNo",yxBill.getBillNo());
        billRefundMapper.deleteByExample(refundExample);
        Example productExample = new Example(BillProduct.class);
        productExample.createCriteria().andEqualTo("billNo",yxBill.getBillNo());
        billProductMapper.deleteByExample(productExample);
        //重新计算账单
        BillMoneyDTO dto = saveOrderAndAfterSale(yxBill.getBillNo(), yxBill.getBankerId(), startTime, endTime);
        //金额保存起来
        updateBill(yxBill,dto);
//        //重新从大数据中拉取金额并保持起来
//        getAmountByBigData(yxBill);
//        //把原本标记的信息去掉
//        orderKafkaRecordMapper.updateRecord(yxBill.getId());
//        //把传送kafka记录表标记起来
//        signRecord(yxBill.getBankerId(), yxBill.getId(),yxBill.getBillStartTime(),yxBill.getBillEndTime());
        return CommonResult.success();
    }

    public CommonResult list(BankerBillRequest request) {
        Example example = new Example(YxBill.class);
        example.createCriteria().andEqualTo("bankerId",request.getBankerId());
        example.setOrderByClause("id desc");
        PageHelper.startPage(request.getPageNum(),request.getPageSize());
        List<YxBill> yxBills = billMapper.selectByExample(example);
        if (!CollectionUtils.isEmpty(yxBills)){
            List<Integer> billIds = yxBills.stream().map(YxBill::getId).collect(Collectors.toList());
            Example payExample = new Example(YxBillPayment.class);
            payExample.createCriteria().andIn("billId",billIds);
            List<YxBillPayment> yxBillPayments = billPaymentMapper.selectByExample(payExample);
            if (!CollectionUtils.isEmpty(yxBillPayments)){
                yxBills.forEach(bill ->{
                    Optional<YxBillPayment> first = yxBillPayments.stream().filter(pay -> bill.getId().equals(pay.getBillId())).findFirst();
                    bill.setYxBillPayment(first.orElse(null));
                });
            }
        }
        return CommonResult.success(CommonPage.restPage(yxBills));
    }

    public CommonResult savePayment(BillPaymentRequest request) {
        YxBill yxBill = billMapper.selectByPrimaryKey(request.getBillId());
        if (1 == yxBill.getBillStatus()){
            return CommonResult.failed("对账未完成的账单不能填写打款信息");
        }
        YxBillPayment billPayment = billPaymentMapper.selectByPrimaryKey(request.getId());
        BeanUtils.copyProperties(request,billPayment);
        billPaymentMapper.updateByPrimaryKeySelective(billPayment);
        if (1 == request.getPaymentStatus()){
            yxBill.setBillStatus(3);
        }
        billMapper.updateByPrimaryKeySelective(yxBill);
        return CommonResult.success();
    }

    public CommonResult success(Integer billId) {
        YxBill yxBill = billMapper.selectByPrimaryKey(billId);
        if (1 != yxBill.getBillStatus()){
            return CommonResult.failed("账单对账已完成,不能修改");
        }
        Example orderExample = new Example(BillOrder.class);
        orderExample.createCriteria().andEqualTo("billNo",yxBill.getBillNo());
        orderExample.selectProperties("childOrderNo");
        List<BillOrder> billOrders = billOrderMapper.selectByExample(orderExample);
        if (!CollectionUtils.isEmpty(billOrders)){
            List<String> childOrderNos = billOrders.stream().map(BillOrder::getChildOrderNo).collect(Collectors.toList());
            ordersMapper.updateOrderReconciliationStatus(childOrderNos);
        }

        Example refundExample = new Example(BillRefund.class);
        orderExample.selectProperties("childOrderNo");
        refundExample.createCriteria().andEqualTo("billNo",yxBill.getBillNo());
        List<BillRefund> billRefunds = billRefundMapper.selectByExample(refundExample);
        if (!CollectionUtils.isEmpty(billRefunds)){
            List<String> childOrderNos = billRefunds.stream().map(BillRefund::getChildOrderNo).collect(Collectors.toList());
            ordersMapper.updateRefundReconciliationStatus(childOrderNos);
        }
        yxBill.setBillStatus(2);
        billMapper.updateByPrimaryKeySelective(yxBill);
        return CommonResult.success();
    }

    public CommonResult systemReconciliation(Integer billId) {
        YxBill yxBill = billMapper.selectByPrimaryKey(billId);
        if (1 != yxBill.getBillStatus()){
            return CommonResult.failed("已对账的账单不能重复发起系统对账");
        }
        yxBill.setCheckTime(new Date());
        billMapper.updateByPrimaryKeySelective(yxBill);
        //把原本账单信息删除
        Example orderExample = new Example(BillOrder.class);
        orderExample.createCriteria().andEqualTo("billNo",yxBill.getBillNo());
        billOrderMapper.deleteByExample(orderExample);
        Example refundExample = new Example(BillRefund.class);
        refundExample.createCriteria().andEqualTo("billNo",yxBill.getBillNo());
        billRefundMapper.deleteByExample(refundExample);
        //重新计算账单
        String startTime = yxBill.getBillStartTime() + " 00:00:00";
        String endTime = yxBill.getBillEndTime() + " 23:59:59";
        BillMoneyDTO dto = saveOrderAndAfterSale(yxBill.getBillNo(), yxBill.getBankerId(), startTime, endTime);
        //金额保存起来
        updateBill(yxBill,dto);
        return CommonResult.success();
    }
    /*
    * 供应商对账单>对账单详情
    * 1、根据id查询yx_bill，得到bill_no
    * 2、再根据bill_no查询yx_bill_order,yx_bill_refund
    * 3、根据project分别算出账单信息
    * 4、计算逻辑
    *       应退货款：
	        yx_bill_refund.order_reconciliation_status=1 取cost_price
	        yx_bill_refund.order_reconciliation_status=0 取return_duty，判断return_duty=0，如是取warehouse_bear_fee，否则0
            本期退货运费：
            freight_duty=0，取freight_duty_fee
            freight_duty!=0,取0
            本期应付货款=汇总cost_price
            本期应付运费=汇总freight
    * */
    public CommonResult getStatementDetails(BillOrderRequest request){
        log.info("====getStatementDetails==={}", request);
        StatementDetailsResp resp=new StatementDetailsResp();
        List<BillOrderResp> list=new ArrayList<>();

        //得到bill_no
        YxBill yxBill=billMapper.getYxBill(request);
        if(null==yxBill){
            return CommonResult.success(list);
        }
        //淘心购
        BillOrderResp billOrderRespByC=getBillOrderResp(yxBill.getBillNo(),ProjectEnum.TXG.getCode());
        billOrderRespByC.setProject(ProjectEnum.TXG.getMessage());
        list.add(billOrderRespByC);
        //全名严选
        BillOrderResp billOrderRespByO=getBillOrderResp(yxBill.getBillNo(),ProjectEnum.YX.getCode());
        billOrderRespByO.setProject(ProjectEnum.YX.getMessage());
        list.add(billOrderRespByO);
        log.info("====getStatementDetails.end==={}", list);
        BigDecimal actuallyAmountTotal=new BigDecimal("0");
        for(BillOrderResp model:list){
            if(null!=model.getActuallyAmount()){
                actuallyAmountTotal=actuallyAmountTotal.add(model.getActuallyAmount());
            }
        }
        resp.setBillOrderResp(list);
        //本期账单总金额
        resp.setActuallyAmountTotal(actuallyAmountTotal);
        return CommonResult.success(resp);
    }
    /**
     * 分页查询应付货款、 应付运费
     * */
    public void getBillOrderRespByPageO(BillOrderResp billOrderResp,String billNo,Integer project){
        int pageSize=500;
        BillOrderRequest billOrder=new BillOrderRequest();
        billOrder.setBillNo(billNo);
        billOrder.setProject(project);
        int totalCount=billOrderMapper.selectBillOrderCountByPage(billOrder);
        billOrderResp.setPaymentDue(new BigDecimal("0"));
        billOrderResp.setPaymentFee(new BigDecimal("0"));
        if(totalCount>0){
            int pageCount = totalCount%pageSize == 0 ? totalCount/pageSize : totalCount/pageSize + 1;
            int currentPage=1;
            while(currentPage<=pageCount){
                billOrder.setPageSize(pageSize);
                billOrder.setPageNum(currentPage);
                PageHelper.startPage(billOrder.getPageNum(),billOrder.getPageSize());
                List<BillOrder> billOrderListByO=billOrderMapper.selectBillOrderByPage(billOrder);
                if(CollectionUtils.isEmpty(billOrderListByO)){
                    break;
                }
                BillOrderResp billOrderRespTemp=getBillOrderRespByBillOrder(billOrderListByO);
                billOrderResp.setPaymentDue(billOrderRespTemp.getPaymentDue().add(billOrderResp.getPaymentDue()));
                billOrderResp.setPaymentFee(billOrderRespTemp.getPaymentFee().add(billOrderResp.getPaymentFee()));
                currentPage++;
            }
        }
    }
    /**
     * 分页查询应退货款、 本期退货运费
     * */
    public void getBillOrderRespByPageC(BillOrderResp billOrderResp,String billNo,Integer project){
        int pageSize=500;
        BillOrderRequest billOrder=new BillOrderRequest();
        billOrder.setBillNo(billNo);
        billOrder.setProject(project);
        int totalCount=billRefundMapper.selectBillRefundCountByPage(billOrder);
        billOrderResp.setRefundAmount(new BigDecimal("0"));
        billOrderResp.setRefundFreight(new BigDecimal("0"));
        if(totalCount>0){
            int pageCount = totalCount%pageSize == 0 ? totalCount/pageSize : totalCount/pageSize + 1;
            int currentPage=1;
            while(currentPage<=pageCount){
                billOrder.setPageSize(pageSize);
                billOrder.setPageNum(currentPage);
                PageHelper.startPage(billOrder.getPageNum(),billOrder.getPageSize());
                List<BillRefund> list=billRefundMapper.selectBillRefundByPage(billOrder);
                if(CollectionUtils.isEmpty(list)){
                    break;
                }
                BillOrderResp billOrderRespTemp=getBillOrderRespByBillRefund(list);
                billOrderResp.setRefundAmount(billOrderRespTemp.getRefundAmount().add(billOrderResp.getRefundAmount()));
                billOrderResp.setRefundFreight(billOrderRespTemp.getRefundFreight().add(billOrderResp.getRefundFreight()));
                currentPage++;
            }
        }
    }
    /**
     * 本期应付总额、本期实付金额
     * */
    public BillOrderResp getBillOrderResp(String billNo,Integer project){
        BillOrderResp billOrderResp=new BillOrderResp();
        getBillOrderRespByPageO(billOrderResp,billNo,project);
        getBillOrderRespByPageC(billOrderResp,billNo,project);
        billOrderResp.setAmountTotal(billOrderResp.getPaymentDue().add(billOrderResp.getPaymentFee()));
        billOrderResp.setActuallyAmount(billOrderResp.getAmountTotal().subtract(billOrderResp.getRefundAmount()).subtract(billOrderResp.getRefundFreight()));
        return billOrderResp;
    }

    /**
     * 应付货款、 应付运费
     */
    public BillOrderResp getBillOrderRespByBillOrder(List<BillOrder> billOrderList){
        BillOrderResp billOrderResp=new BillOrderResp();
        BigDecimal costPriceSum=new BigDecimal("0");
        BigDecimal freightSum=new BigDecimal("0");
        if(!CollectionUtils.isEmpty(billOrderList)){
            costPriceSum = billOrderList.stream().map(BillOrder::getCostPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
            freightSum = billOrderList.stream().filter(e->null!=e.getFreight()).map(BillOrder::getFreight).reduce(BigDecimal.ZERO, BigDecimal::add);
        }
        billOrderResp.setPaymentDue(costPriceSum);
        billOrderResp.setPaymentFee(freightSum);
        return billOrderResp;
    }
    /**
     * 得到应退货款、 本期退货运费
     */
    public BillOrderResp getBillOrderRespByBillRefund(List<BillRefund> billRefundList){
        BillOrderResp billOrderResp=new BillOrderResp();
        //应退货款
        BigDecimal refundAmount=new BigDecimal("0");
        //应退货款：yx_bill_refund.order_reconciliation_status=1 取cost_price
        BigDecimal costPriceSum=billRefundList.stream().filter(item->OrderReconciliationEnum.ALREADY_CHECK.getCode().equals(item.getOrderReconciliationStatus())).
                map(BillRefund::getCostPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
        //应退货款： yx_bill_refund.order_reconciliation_status=0 取return_duty，判断return_duty=0，如是取warehouse_bear_fee，否则0
        List<BillRefund> noOrderReconciliationList=billRefundList.stream().
                filter(item->!OrderReconciliationEnum.ALREADY_CHECK.getCode().equals(item.getOrderReconciliationStatus())).collect(Collectors.toList());
        if(!CollectionUtils.isEmpty(noOrderReconciliationList)){
            refundAmount=billRefundList.stream().filter(item->ReturnDutyEnum.WAREHOUSE.getCode().equals(item.getReturnDuty())).
                    map(BillRefund::getWarehouseBearFee).reduce(BigDecimal.ZERO, BigDecimal::add);
        }
        refundAmount=refundAmount.add(costPriceSum);
        // 本期退货运费
        BigDecimal refundFreight=billRefundList.stream().filter(item->FreightDutyEnum.WAREHOUSE.getCode().equals(item.getFreightDuty())).
                map(BillRefund::getFreightDutyFee).reduce(BigDecimal.ZERO, BigDecimal::add);
        billOrderResp.setRefundAmount(refundAmount);
        billOrderResp.setRefundFreight(refundFreight);
        return billOrderResp;
    }

    public CommonResult getBillSummary(BillSummaryRequest request) {
        log.info("====BillService.getBillSummary==={}", request);
        PageHelper.startPage(request.getPageNum(),request.getPageSize());
        List<BillSummaryResponse> list = billProductMapper.selectByRequest(request);
        CommonPage<BillSummaryResponse> page = CommonPage.restPage(list);
        if (CollectionUtils.isEmpty(list)){
            return CommonResult.success(page);
        }
        List<Integer> skuIds = list.stream().map(BillSummaryResponse::getSkuId).collect(Collectors.toList());
        Example billOrderExample = new Example(BillOrder.class);
        billOrderExample.createCriteria().andEqualTo("billNo",request.getBillNo()).andIn("skuId",skuIds);
        billOrderExample.selectProperties("skuId","costPrice","freight");
        List<BillOrder> billOrders = billOrderMapper.selectByExample(billOrderExample);

        Example billRefundExample = new Example(BillRefund.class);
        billRefundExample.createCriteria().andEqualTo("billNo",request.getBillNo()).andIn("skuId",skuIds);
        billRefundExample.selectProperties("skuId","costPrice","freightDuty","freightDutyFee","returnDuty","warehouseBearFee","orderReconciliationStatus");
        List<BillRefund> billRefunds = billRefundMapper.selectByExample(billRefundExample);

        page.setList(list.stream().peek(item ->{
            Integer skuId = Optional.ofNullable(item.getSkuId()).orElse(0);
            BigDecimal payableMoney = billOrders.stream()
                    .filter(order -> null != order.getCostPrice() && skuId.equals(order.getSkuId()))
                    .map(BillOrder::getCostPrice)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            item.setPayableMoney(payableMoney);
            BigDecimal refundableMoney1 =  billRefunds.stream()
                    .filter(refund -> null != refund.getWarehouseBearFee() && OrderReconciliationEnum.NO_CHECK.getCode().equals(refund.getOrderReconciliationStatus()) && skuId.equals(refund.getSkuId()) &&null != refund.getReturnDuty() && ReturnDutyEnum.WAREHOUSE.getCode().equals(refund.getReturnDuty()))
                    .map(BillRefund::getWarehouseBearFee)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal refundableMoney2 = billRefunds.stream()
                    .filter(refund ->  null != refund.getCostPrice() && OrderReconciliationEnum.ALREADY_CHECK.getCode().equals(refund.getOrderReconciliationStatus()) && skuId.equals(refund.getSkuId()) )
                    .map(BillRefund::getCostPrice)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            item.setRefundableMoney(refundableMoney1.add(refundableMoney2));
            item.setPaymentMoney(item.getPayableMoney().subtract(item.getRefundableMoney()));

            BigDecimal payFreightMoney = billOrders.stream()
                    .filter(order -> null != order.getFreight() && skuId.equals(order.getSkuId()) && null != order.getFreight())
                    .map(BillOrder::getFreight)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            item.setPayFreightMoney(payFreightMoney);
            BigDecimal refundFreightMoney =  billRefunds.stream()
                    .filter(refund -> null != refund.getFreightDutyFee() && OrderReconciliationEnum.NO_CHECK.getCode().equals(refund.getOrderReconciliationStatus()) && skuId.equals(refund.getSkuId()) &&null != refund.getFreightDuty()&& FreightDutyEnum.WAREHOUSE.getCode().equals(refund.getFreightDuty()))
                    .map(BillRefund::getFreightDutyFee)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            item.setRefundFreightMoney(refundFreightMoney);
            item.setFreightMoney(item.getPayFreightMoney().subtract(item.getRefundFreightMoney()));
        }).collect(Collectors.toList()));
        return CommonResult.success(page);
    }

    public CommonPage<StatementListResp> selectList(BillOrderRequest request) {
        log.info("====BillService.selectList==={}", request);
        List<StatementListResp> pageList=new ArrayList<>();
        //得到bill_no
        YxBill yxBill=billMapper.getYxBill(request);
        if(null==yxBill){
            return CommonPage.restPage(pageList);
        }
        PageHelper.startPage(request.getPageNum(), request.getPageSize());
        request.setBillNo(yxBill.getBillNo());
        if(StatementTypeEnum.FINISH_ORDER.getCode()==request.getType()){
            pageList = billOrderMapper.selectList(request);
        }
        if(StatementTypeEnum.CLOSE_ORDER.getCode()==request.getType()){
            request.setOrderReconciliationStatus(OrderReconciliationEnum.NO_CHECK.getCode());
            pageList =billRefundMapper.selectList(request);
        }
        if(StatementTypeEnum.BEFORE_ORDER.getCode()==request.getType()){
            request.setOrderReconciliationStatus(OrderReconciliationEnum.ALREADY_CHECK.getCode());
            pageList =billRefundMapper.selectList(request);
        }
        pageList.forEach(item->{
            BigDecimal freight = Optional.ofNullable(item.getFreight()).orElse(BigDecimal.ZERO);
            item.setAmountTotal(item.getCostPrice().add(freight));
            if(null!=item.getFreightDuty()){
                item.setFreightDutyDesc(FreightDutyEnum.getMessageByCode(item.getFreightDuty()));
            }
            if(null!=item.getReturnDuty()){
                item.setReturnDutyDesc(ReturnDutyEnum.getMessageByCode(item.getReturnDuty()));
            }
        });
        return CommonPage.restPage(pageList);
    }

    public CommonResult download(BillOrderRequest request) {
        List<StatementListResp> list = billOrderMapper.selectList(request);
        List<StatementListResp> refundList = billRefundMapper.selectList(request);
        list.addAll(refundList);
        list.forEach(item->{
            BigDecimal freight = Optional.ofNullable(item.getFreight()).orElse(BigDecimal.ZERO);
            item.setAmountTotal(item.getCostPrice().add(freight));
            if(null!=item.getFreightDuty()){
                item.setFreightDutyDesc(FreightDutyEnum.getMessageByCode(item.getFreightDuty()));
            }
            if(null!=item.getReturnDuty()){
                item.setReturnDutyDesc(ReturnDutyEnum.getMessageByCode(item.getReturnDuty()));
            }
            //订单状态 3.已完成 4.已关闭
            if (null != item.getCloseTime()){
                item.setOrderStatus(OrdersStatusEnum.CLOSE.getCode());
            }else {
                item.setOrderStatus(OrdersStatusEnum.FINISH.getCode());
            }
        });
        return CommonResult.success(list);
    }

    /**
     * 获取供应商创建对账订单的参数
     * @param bankerId
     * @return
     */
    public CommonResult<BankerBillVO> getBankerMsg(Integer bankerId) {
        BankerBillVO vo = new BankerBillVO();
        BankerBillResponse bankerInfo = bankerService.getBankerById(bankerId);
        Date date = new Date();
        //获取今天时间是周几
        String weekDay = getWeekDay();
        //今天的日期
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        String todayDate = sdf.format(date);
        vo.setBankerId(bankerInfo.getBankerId());
        vo.setRepayType(bankerInfo.getRepayType());
        vo.setBillOrderType(bankerInfo.getBillOrderType());
        vo.setWeekDay(weekDay);
        vo.setTodayDate(todayDate);

        Example example = new Example(YxBill.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("bankerId", bankerId);
        example.selectProperties("billStartTime", "billEndTime");
        List<YxBill> yxBills = billMapper.selectByExample(example);

        //时间集合
        List<BillDateVO> list = new ArrayList<>();

        if (2 == bankerInfo.getRepayType()){
            getDateByWeek(date, sdf, yxBills, list, bankerInfo.getCreateTime());
        }else if (3 == bankerInfo.getRepayType()){
            getDateByHalfMonth(date, sdf, yxBills, list,bankerInfo.getCreateTime());
        }else if (4 == bankerInfo.getRepayType()){
            getDateByMonth(date, sdf, yxBills, list,bankerInfo.getCreateTime());
        }

        vo.setBillDateCycleList(list);
        return CommonResult.success(vo);
    }

    /**
     * 结算类型为月结，获取所有周期
     * @param date
     * @param sdf
     * @param yxBills
     * @param list
     * @param createTime
     * @return
     */
    private void getDateByMonth(Date date, SimpleDateFormat sdf, List<YxBill> yxBills, List<BillDateVO> list, Date createTime) {
        //临时开始日期
        Date tempDate = null;
        //今日日期
        String format = sdf.format(date);
        for (int i = 0; i < 50; i++) {
            if (null != tempDate && tempDate.getTime() < createTime.getTime()){
                continue;
            }
            AtomicReference<Boolean> flag = new AtomicReference<>(false);
            BillDateVO billDateVO = new BillDateVO();
            String s = format.substring(0, 8) + "01";
            try {
                date = sdf.parse(s);
            } catch (ParseException e) {
                e.printStackTrace();
            }
            Calendar cal = Calendar.getInstance();
            cal.setTime(date);
            cal.add(Calendar.MONTH,-i);
            cal.add(Calendar.DAY_OF_MONTH,-1);
            Date time = cal.getTime();
            //这个月最后一天
            String monthLastDate = sdf.format(time);
            //这个月的第一天
            String monthFirstDate = monthLastDate.substring(0, 8) + "01";
            billDateVO.setStartDate(monthFirstDate);
            billDateVO.setEndDate(monthLastDate);
            try {
                //查看是否与之前的账单时间存在交集
                Boolean aBoolean = checkBillTime(yxBills, billDateVO.getStartDate(), billDateVO.getEndDate(), flag);
                billDateVO.setIsIntersection(aBoolean ? 1:0);
            } catch (ParseException e) {
                e.printStackTrace();
            }
            list.add(billDateVO);
            try {
                //修改临时字段用于判断
                tempDate = sdf.parse(billDateVO.getStartDate());
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     *  结算类型为半月结，获取所有周期
     * @param date
     * @param sdf
     * @param yxBills
     * @param list
     * @param createTime
     */
    private void getDateByHalfMonth(Date date, SimpleDateFormat sdf, List<YxBill> yxBills, List<BillDateVO> list, Date createTime) {
        //今日日期
        String format = sdf.format(date);

        //临时开始日期
        Date tempDate = null;
        //判断是时间为上半月还是下半月
        String substring1 =  "1" + (null == tempDate ?format:sdf.format(tempDate)).substring(8);
        Integer num = Integer.valueOf(substring1);
        boolean flag1 = false;
        if (num > 115){
            flag1 = true;
        }
        //临时标记
        Integer j = 0;
        for (int i = 0; i < 100; i++) {
            if (null != tempDate && tempDate.getTime() < createTime.getTime()){
                continue;
            }

            AtomicReference<Boolean> flag = new AtomicReference<>(false);
            BillDateVO billDateVO = new BillDateVO();
            if (!flag1){
                //现在为上半月，获取上个月的下半月的日期
                String s = format.substring(0, 8) + "01";
                try {
                    date = sdf.parse(s);
                } catch (ParseException e) {
                    e.printStackTrace();
                }
                Calendar cal = Calendar.getInstance();
                cal.setTime(date);
                cal.add(Calendar.MONTH,-j);
                cal.add(Calendar.DAY_OF_MONTH,-1);
                Date time = cal.getTime();
                j++;
                //这个下半月最后一天
                String monthLastDate = sdf.format(time);
                //这个下半月的第一天
                String monthFirstDate = monthLastDate.substring(0, 8) + "16";
                billDateVO.setStartDate(monthFirstDate);
                billDateVO.setEndDate(monthLastDate);
                try {
                    //查看是否与之前的账单时间存在交集
                    Boolean aBoolean = checkBillTime(yxBills, billDateVO.getStartDate(), billDateVO.getEndDate(), flag);
                    billDateVO.setIsIntersection(aBoolean ? 1:0);
                } catch (ParseException e) {
                    e.printStackTrace();
                }
                try {
                    //修改临时字段用于判断
                    tempDate = sdf.parse(billDateVO.getStartDate());
                } catch (ParseException e) {
                    e.printStackTrace();
                }
                list.add(billDateVO);
                flag1 = true;
            }else {
                String substring = (null == tempDate ? format : sdf.format(tempDate)).substring(0, 8);
                //现在为下半月，获取上个月的下半月
                billDateVO.setStartDate(substring + "01");
                billDateVO.setEndDate(substring + "15");
                try {
                    //查看是否与之前的账单时间存在交集
                    Boolean aBoolean = checkBillTime(yxBills, billDateVO.getStartDate(), billDateVO.getEndDate(), flag);
                    billDateVO.setIsIntersection(aBoolean ? 1:0);
                } catch (ParseException e) {
                    e.printStackTrace();
                }
                try {
                    //修改临时字段用于判断
                    tempDate = sdf.parse(billDateVO.getStartDate());
                } catch (ParseException e) {
                    e.printStackTrace();
                }
                flag1 = false;
                list.add(billDateVO);
            }
        }
    }

    /**
     * 结算类型为周结，获取所有周期
     * @param date
     * @param sdf
     * @param yxBills
     * @param list
     */
    private void getDateByWeek(Date date, SimpleDateFormat sdf, List<YxBill> yxBills, List<BillDateVO> list, Date createTime) {
        //年份
        int year = getYear(date);
        //今年第几周
        int week = getWeek(date);
        //计算用超出范围的周数
        Integer j = 0;
        //去年的周数
        int weekByYear = getWeekNumByYear(year - 1);
        //临时开始日期
        Date tempDate = null;
        //生成周期的日期
        for (int i = 0; i < 1000; i++) {
            if (null != tempDate && tempDate.getTime() < createTime.getTime()){
                continue;
            }
            BillDateVO billDateVO = new BillDateVO();
            AtomicReference<Boolean> flag = new AtomicReference<>(false);
            int nowWeek = week - i;
            if (nowWeek > 0){
                Date firstDayOfWeek = getYearWeekFirstDay(year, nowWeek);
                tempDate = firstDayOfWeek;
                String startDate = sdf.format(firstDayOfWeek);
                billDateVO.setStartDate(startDate);
                Date lastDayOfWeek = getYearWeekEndDay(year, nowWeek);
                String endDate = sdf.format(lastDayOfWeek);
                billDateVO.setEndDate(endDate);
                try {
                    //查看是否与之前的账单时间存在交集
                    Boolean aBoolean = checkBillTime(yxBills, startDate, endDate, flag);
                    billDateVO.setIsIntersection(aBoolean ? 1:0);
                } catch (ParseException e) {
                    e.printStackTrace();
                }
            }else {
                int newWeekNum = weekByYear - j;
                j++;
                Date firstDayOfWeek = getYearWeekFirstDay(year - 1, newWeekNum);
                tempDate = firstDayOfWeek;
                String startDate = sdf.format(firstDayOfWeek);
                billDateVO.setStartDate(startDate);
                Date lastDayOfWeek = getYearWeekEndDay(year - 1, newWeekNum);
                String endDate = sdf.format(lastDayOfWeek);
                billDateVO.setEndDate(endDate);
                try {
                    //查看是否与之前的账单时间存在交集
                    Boolean aBoolean = checkBillTime(yxBills, startDate, endDate, flag);
                    billDateVO.setIsIntersection(aBoolean ? 1:0);
                } catch (ParseException e) {
                    e.printStackTrace();
                }
            }
            list.add(billDateVO);
        }
    }

    /**
     * 获取今天时间是周几
     * @return
     */
    private String getWeekDay(){
        String[] weekDays = {"周日", "周一", "周二", "周三", "周四", "周五", "周六"};
        //创建日历类
        Calendar calendar = Calendar.getInstance();
        //获取当月月份
        int month = calendar.get(Calendar.MONTH) + 1;
        //设置当前月份
        calendar.set(0, month, 0);
        //设置当前时间
        calendar.setTime(new Date());
        //今天是周几
        return weekDays[calendar.get(Calendar.DAY_OF_WEEK)-1];
    }

    /**
     * 计算指定年度共有多少个周。
     * @param year 格式 yyyy ，必须大于1900年度 小于9999年
     * @return
     */

    public static int getWeekNumByYear(final int year){
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        int result = 52;//每年至少有52个周 ，最多有53个周。
        String date = sdf.format(getYearWeekFirstDay(year,53));
        if(date.substring(0, 4).equals(year+"")){ //判断年度是否相符，如果相符说明有53个周。
            result = 53;
        }
        return result;
    }

    /**
     * 计算某年某周的开始日期
     * @param yearNum 格式 yyyy ，必须大于1900年度 小于9999年
     * @param weekNum 1到52或者53
     * @return 日期，格式为yyyy-MM-dd
     */

    public static Date getYearWeekFirstDay(int yearNum,int weekNum) {
        Calendar cal = Calendar.getInstance();
        //设置每周的第一天为星期一
        cal.setFirstDayOfWeek(Calendar.MONDAY);
        //每周从周一开始
        cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
        // 上面两句代码配合，才能实现，每年度的第一个周，是包含第一个星期一的那个周。
        //设置每周最少为7天
        cal.setMinimalDaysInFirstWeek(7);
        cal.set(Calendar.YEAR, yearNum);
        cal.set(Calendar.WEEK_OF_YEAR, weekNum);
        //分别取得当前日期的年、月、日
        return cal.getTime();
    }

    /**
     * 计算某年某周的结束日期
     * @param yearNum 格式 yyyy ，必须大于1900年度 小于9999年
     * @param weekNum 1到52或者53
     * @return 日期，格式为yyyy-MM-dd
     */

    public static Date getYearWeekEndDay(int yearNum,int weekNum) {
        Calendar cal = Calendar.getInstance();
        //设置每周的第一天为星期一
        cal.setFirstDayOfWeek(Calendar.MONDAY);
        //每周从周一开始
        cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
        // 上面两句代码配合，才能实现，每年度的第一个周，是包含第一个星期一的那个周。
        //设置每周最少为7天
        cal.setMinimalDaysInFirstWeek(7);
        cal.set(Calendar.YEAR, yearNum);
        cal.set(Calendar.WEEK_OF_YEAR, weekNum);
        return cal.getTime();
    }

    /**
     * 获取现在的周数
     * @param date
     * @return
     */
    public static int getWeek(Date date) {
        GregorianCalendar g = new GregorianCalendar();
        g.setTime(date);
        return g.get(Calendar.WEEK_OF_YEAR) - 1; //获得周数
    }
    /**
     * 获取年份
     * @param date
     * @return
     */
    public static int getYear(Date date) {
        GregorianCalendar g = new GregorianCalendar();
        g.setTime(date);
        return g.get(Calendar.YEAR); //获取年份
    }
}
